/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.data.source;

import io.iec.edp.caf.common.JSONSerializer;
import io.iec.edp.caf.commons.dataaccess.*;
import io.iec.edp.caf.commons.dataaccess.dm.DmConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.dm.DmDbConfigData;
import io.iec.edp.caf.commons.dataaccess.gbase.GbaseConfigData;
import io.iec.edp.caf.commons.dataaccess.gbase.GbaseConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.kingbase.KingbaseConfigData;
import io.iec.edp.caf.commons.dataaccess.kingbase.KingbaseConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.mysql.MySqlConfigData;
import io.iec.edp.caf.commons.dataaccess.mysql.MySqlConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.oracle.OracleConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.oracle.OracleDbConfigData;
import io.iec.edp.caf.commons.dataaccess.oscar.OscarConfigData;
import io.iec.edp.caf.commons.dataaccess.oscar.OscarConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.pgsql.PgSqlConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.pgsql.PostgreSQLConfigData;
import io.iec.edp.caf.commons.dataaccess.sqlserver.SqlConnectionStringBuilder;
import io.iec.edp.caf.commons.dataaccess.sqlserver.SqlDbConfigData;
import io.iec.edp.caf.commons.utils.StringUtils;
import io.iec.edp.caf.data.source.db2.DB2ServiceInfo;
import io.iec.edp.caf.data.source.dm.DmServiceInfo;
import io.iec.edp.caf.data.source.gauss.GaussDB4PgServiceInfo;
import io.iec.edp.caf.data.source.gauss.OpenGaussServiceInfo;
import io.iec.edp.caf.data.source.gbase.GbaseServiceInfo;
import io.iec.edp.caf.data.source.gbase8c.Gbase8cServiceInfo;
import io.iec.edp.caf.data.source.gbase8s.Gbase8sServiceInfo;
import io.iec.edp.caf.data.source.highgo.HighGoServiceInfo;
import io.iec.edp.caf.data.source.kingbase.KingbaseServiceInfo;
import io.iec.edp.caf.data.source.mysql.MysqlServiceInfo;
import io.iec.edp.caf.data.source.ocean.OceanBaseServiceInfo;
import io.iec.edp.caf.data.source.oracle.OracleServiceInfo;
import io.iec.edp.caf.data.source.oscar.OscarServiceInfo;
import io.iec.edp.caf.data.source.pgsql.PostgresqlServiceInfo;
import io.iec.edp.caf.data.source.sqlserver.SqlServerServiceInfo;

/**
 *
 * @author wangyandong
 * @date 2020/04/22 下午 03:22
 *
 */
public class DbConfigDataConvertor {

    /**
     * 将EcpDbConnection中的连接字符串转为jdbcUrl格式
      * @param configData
     * @return
     */
    public static JDBCConnectionInfo convert(SimpleDbConfigData configData){
        if(configData ==null || StringUtils.isEmpty(configData.getConnectionString()))
            return null;

        JDBCConnectionInfo info = null;
        if(configData.getConnectionString().startsWith("jdbc:")){
          //解密UriString
          String uriString = UriDecryptor.decrypt(configData.getConnectionString());
          RelationalServiceInfo serviceInfo = RelationalServiceInfoFactory.create(uriString);
            info = JDBCConnectionInfo.builder()
                  .jdbcUrl(serviceInfo.getJdbcUrl())
                  .userName(serviceInfo.getUserName())
                  .password(serviceInfo.getPassword())
                  .driverClassName(serviceInfo.getDriverClassName())
                  .build();
        }else if(configData.getConnectionString().startsWith("custom:")){
            //custom开头的是对CustomConnectionInfo(只塞上jdbcUrl,username,password值）对象序列化后加密的
            //解密UriString
            String uriString = UriDecryptor.decrypt(configData.getConnectionString());
            CustomConnectionInfo customConnectionInfo= JSONSerializer.deserialize(uriString,CustomConnectionInfo.class);
            String driverClassName=getDriverClassName(configData.getDbType());
            info= JDBCConnectionInfo.builder()
                    .jdbcUrl(customConnectionInfo.getJdbcUrl())
                    .userName(customConnectionInfo.getUserName())
                    .password(customConnectionInfo.getPassword())
                    .driverClassName(driverClassName)
                    .build();
        }else{
            //将DotNet版的连接字符串转为通用配置对象
            GSPDbConfigData data = convertFromDotNET(configData);
            //通用配置对象转为JDBC对象
            info = convert(data);
        }
        return info;
    }

    /// <summary>
    /// 将ADO.NET形式的连接字符串转为通用配置对象
    /// </summary>
    /// <param name="dataSource">数据源信息</param>
    /// <returns>IGSPDatabase接口 data/Source为null返回Null</returns>
    public static GSPDbConfigData convertFromDotNET(SimpleDbConfigData dataSource){
        if (dataSource == null)
            return null;
        //拦截CommandTimeout属性值
        String connectionString = dataSource.getConnectionString();

        GSPDbConfigData configdata = null;

        switch (dataSource.getDbType()) {
            case PgSQL:
                configdata = new PostgreSQLConfigData();
                PgSqlConnectionStringBuilder npgBuilder = new PgSqlConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(npgBuilder.getUser()));
                configdata.setPassword(Utils.decrypt(npgBuilder.getPassword()));
                configdata.setSource(Utils.decrypt(npgBuilder.getServer()) + ":" + npgBuilder.getPort());
                configdata.setCatalog(Utils.decrypt(npgBuilder.getDatabase()));
                configdata.setConnectTimeout(npgBuilder.getConnectionTimeout());
                configdata.setCommandTimeout(npgBuilder.getCommandTimeout());
                configdata.setMaxPoolSize(npgBuilder.getPoolSize());
                break;
            case SQLServer:
                configdata = new SqlDbConfigData();
                SqlConnectionStringBuilder sqlbuilder = new SqlConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(sqlbuilder.getUser()));
                configdata.setPassword(Utils.decrypt(sqlbuilder.getPassword()));
                configdata.setSource(Utils.decrypt(sqlbuilder.getServer()));
                configdata.setCatalog(Utils.decrypt(sqlbuilder.getDatabase()));
                configdata.setConnectTimeout(sqlbuilder.getConnectionTimeout());
                configdata.setCommandTimeout(sqlbuilder.getCommandTimeout());
                configdata.setMaxPoolSize(sqlbuilder.getPoolSize());
                break;
            case Oracle:
                configdata = new OracleDbConfigData();
                OracleConnectionStringBuilder orabuilder = new OracleConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(orabuilder.getUser()));
                configdata.setPassword(Utils.decrypt(orabuilder.getPassword()));
                configdata.setSource(Utils.decrypt(orabuilder.getServer()));
                configdata.setConnectTimeout(orabuilder.getConnectionTimeout());
                configdata.setCommandTimeout(orabuilder.getCommandTimeout());
                configdata.setMaxPoolSize(orabuilder.getPoolSize());
                break;
            case DM:
                configdata = new DmDbConfigData();
                DmConnectionStringBuilder dmbuilder = new DmConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(dmbuilder.getUser()));
                configdata.setPassword(Utils.decrypt(dmbuilder.getPassword()));
                configdata.setSource(Utils.decrypt(dmbuilder.getServer()) + ":" + dmbuilder.getPort());
                configdata.setCatalog(Utils.decrypt(dmbuilder.getDatabase()));
                configdata.setConnectTimeout(dmbuilder.getConnectionTimeout());
                configdata.setCommandTimeout(dmbuilder.getCommandTimeout());
                configdata.setMaxPoolSize(dmbuilder.getPoolSize());
                break;
            case MySQL:
                configdata = new MySqlConfigData();
                MySqlConnectionStringBuilder mySqlbuilder = new MySqlConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(mySqlbuilder.getUser()));
                configdata.setPassword(Utils.decrypt(mySqlbuilder.getPassword()));
                configdata.setSource(Utils.decrypt(mySqlbuilder.getServer()));
                configdata.setCatalog(Utils.decrypt(mySqlbuilder.getDatabase()));
                configdata.setConnectTimeout(mySqlbuilder.getConnectionTimeout());
                configdata.setCommandTimeout(mySqlbuilder.getCommandTimeout());
                configdata.setMaxPoolSize(mySqlbuilder.getPoolSize());
                break;
            case Oscar:
                configdata = new OscarConfigData();
                OscarConnectionStringBuilder oscarbuilder = new OscarConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(oscarbuilder.getUser()));
                configdata.setPassword(Utils.decrypt(oscarbuilder.getPassword()));
                configdata.setSource(Utils.decrypt(oscarbuilder.getServer()));
                configdata.setCatalog(Utils.decrypt(oscarbuilder.getDatabase()));
                configdata.setConnectTimeout(oscarbuilder.getConnectionTimeout());
                configdata.setCommandTimeout(oscarbuilder.getCommandTimeout());
                configdata.setMaxPoolSize(oscarbuilder.getPoolSize());
                break;
            case Kingbase:
                configdata = new KingbaseConfigData();
                KingbaseConnectionStringBuilder kingbasebuilder = new KingbaseConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(kingbasebuilder.getUser()));
                configdata.setPassword(Utils.decrypt(kingbasebuilder.getPassword()));
                configdata.setSource(Utils.decrypt(kingbasebuilder.getServer()));
                configdata.setCatalog(Utils.decrypt(kingbasebuilder.getDatabase()));
                configdata.setConnectTimeout(kingbasebuilder.getConnectionTimeout());
                configdata.setCommandTimeout(kingbasebuilder.getCommandTimeout());
                configdata.setMaxPoolSize(kingbasebuilder.getPoolSize());
                break;
            case Gbase:
                configdata = new GbaseConfigData();
                GbaseConnectionStringBuilder gbasebuilder = new GbaseConnectionStringBuilder(connectionString);
                configdata.setUserId(Utils.decrypt(gbasebuilder.getUser()));
                configdata.setPassword(Utils.decrypt(gbasebuilder.getPassword()));
                configdata.setSource(Utils.decrypt(gbasebuilder.getServer()));
                configdata.setCatalog(Utils.decrypt(gbasebuilder.getDatabase()));
                configdata.setConnectTimeout(gbasebuilder.getConnectionTimeout());
                configdata.setCommandTimeout(gbasebuilder.getCommandTimeout());
                configdata.setMaxPoolSize(gbasebuilder.getPoolSize());
                break;
            default:
                break;
        }

        if (configdata != null)
            configdata.setCode(dataSource.getId());

        return configdata;
    }

    /**
     * 将configData转为JDBC的数据库连接对象
     * @param configData
     * @return
     */
    public static JDBCConnectionInfo convert(GSPDbConfigData configData){
        if(configData==null)
            return null;

        String url,driverName;
        switch (configData.getDbType()) {
            case Oracle:
                url = "jdbc:oracle:thin:@//%s";
                driverName  = OracleServiceInfo.ORACLE_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource());
                break;
            case SQLServer:
                url = "jdbc:sqlserver://%s;database=%s;SelectMethod=cursor";
                driverName  = SqlServerServiceInfo.SQLSERVER_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource().replace(",",":"),configData.getCatalog());
                break;
            case PgSQL:
                url = "jdbc:postgresql://%s/%s";
                driverName  = PostgresqlServiceInfo.POSTGRES_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case DM:
                url = "jdbc:dm://%s";
                driverName= DmServiceInfo.DM_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource());
                break;
            case HighGo:
                url = "jdbc:highgo://%s/%s";
                driverName= HighGoServiceInfo.HIGHGO_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case MySQL:
                url = "jdbc:mysql://%s/%s?characterEncoding=utf8&useSSL=false";
                driverName= MysqlServiceInfo.MYSQL_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case Oscar:
                url = "jdbc:oscar://%s/%s";
                driverName= OscarServiceInfo.OSCAR_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case Kingbase:
                url = "jdbc:kingbase8://%s/%s";
                driverName= KingbaseServiceInfo.KINGBASE_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case Gbase:
                url = "jdbc:gbase://%s/%s";
                driverName= GbaseServiceInfo.GBASE_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case DB2:
                url = "jdbc:db2://%s/%s";
                driverName= DB2ServiceInfo.DB2_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case OpenGauss:
                url = "jdbc:opengauss://%s/%s";
                driverName = OpenGaussServiceInfo.OPENGAUSS_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case OceanBase:
                url = "jdbc:oceanbase://%s/%s";
                driverName = OceanBaseServiceInfo.OCEANBASE_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case GBase8s:
                url = "jdbc:gbase8s://%s/%s";
                driverName = Gbase8sServiceInfo.GBASE8S_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case GBase8c:
                url = "jdbc:gbase8c://%s/%s";
                driverName = Gbase8cServiceInfo.GBASE8C_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            case GaussDB:
                url = "jdbc:postgresql://%s/%s";
                driverName  = PostgresqlServiceInfo.POSTGRES_DRIVER_CLASS_NAME;
                url = String.format(url,configData.getSource(),configData.getCatalog());
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + configData.getDbType());
        }
        JDBCConnectionInfo info = JDBCConnectionInfo.builder()
                .jdbcUrl(url)
                .driverClassName(driverName)
                .userName(configData.getUserId())
                .password(configData.getPassword())
                .maxPoolSize(configData.getMaxPoolSize())
                .build();
        return info;
    }

    private static String getDriverClassName(DbType dbType){
        String driverName="";
        switch (dbType) {
            case Oracle:
                driverName  = OracleServiceInfo.ORACLE_DRIVER_CLASS_NAME;
                break;
            case SQLServer:
                driverName  = SqlServerServiceInfo.SQLSERVER_DRIVER_CLASS_NAME;
                break;
            case PgSQL:
                driverName  = PostgresqlServiceInfo.POSTGRES_DRIVER_CLASS_NAME;
                break;
            case DM:
                driverName= DmServiceInfo.DM_DRIVER_CLASS_NAME;
                break;
            case HighGo:
                driverName= HighGoServiceInfo.HIGHGO_DRIVER_CLASS_NAME;
                break;
            case MySQL:
                driverName= MysqlServiceInfo.MYSQL_DRIVER_CLASS_NAME;
                break;
            case Oscar:
                driverName= OscarServiceInfo.OSCAR_DRIVER_CLASS_NAME;
                break;
            case Kingbase:
                driverName= KingbaseServiceInfo.KINGBASE_DRIVER_CLASS_NAME;
                break;
            case Gbase:
                driverName= GbaseServiceInfo.GBASE_DRIVER_CLASS_NAME;
                break;
            case DB2:
                driverName= DB2ServiceInfo.DB2_DRIVER_CLASS_NAME;
                break;
            case OpenGauss:
                driverName = OpenGaussServiceInfo.OPENGAUSS_DRIVER_CLASS_NAME;
                break;
            case OceanBase:
                driverName = OceanBaseServiceInfo.OCEANBASE_DRIVER_CLASS_NAME;
                break;
            case GBase8s:
                driverName = Gbase8sServiceInfo.GBASE8S_DRIVER_CLASS_NAME;
                break;
            case GBase8c:
                driverName = Gbase8cServiceInfo.GBASE8C_DRIVER_CLASS_NAME;
                break;
            case GaussDB:
                driverName  = GaussDB4PgServiceInfo.POSTGRES_DRIVER_CLASS_NAME;
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + dbType);
        }
        return driverName;
    }

}
